package com.ff.xyh.serurity.filter;

import com.ff.xyh.common.utils.JwtUtils;
import com.ff.xyh.common.utils.ResponseUtil;
import com.ff.xyh.common.entity.result.ResultObject;
import com.ff.xyh.serurity.security.TokenManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * <p>
 * 访问过滤器
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {
    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;

    public TokenAuthenticationFilter(AuthenticationManager authManager, TokenManager tokenManager,RedisTemplate redisTemplate) {
        super(authManager);
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        String uri = req.getRequestURI();
//        String method = req.getMethod();
        logger.info("=================" + uri);
        /** 获取用户信息时 检查token 头信息 - 尽量换成 _ */
        String token = req.getHeader("token"); // TokenLogoutHandler.logOut 方法也调用了该请求头
//        System.out.println("method ==> " + method );
//        System.out.println("uri ==> " + uri );
//        System.out.println("token ==> " + token );

        if (StringUtils.isEmpty(token)) {
            SecurityContextHolder.getContext().setAuthentication(null);
            chain.doFilter(req, res);
            return ;
        }
        String newToken = JwtUtils.checkToken(token);
//        System.out.println("newToken ==> " + newToken );
        if (StringUtils.isEmpty(newToken)) {
            SecurityContextHolder.getContext().setAuthentication(null);
            chain.doFilter(req, res);
            return ;
        }
        if (! token.equals(newToken)) { // token更新了
            res.setHeader("Access-Control-Expose-Headers", "new_token");
            res.setHeader("new_token", newToken);
            String userId = JwtUtils.getSubjectFromToken(newToken);
            redisTemplate.opsForValue().set("AccessUserService::" + userId, newToken);
            //                res.setHeader("token", newToken);
            token = newToken;
        }

//        // 如果请求不包含access 则直接放行
//        if(req.getRequestURI().indexOf("access") == -1) {
//            chain.doFilter(req, res);
//            return;
//        }

        UsernamePasswordAuthenticationToken authentication = null;
        try {
            authentication = getAuthentication(token);
//            System.out.println("authentication => getName ==>  " + authentication.getName());
        } catch (Exception e) {
            ResponseUtil.out(res, ResultObject.error().message("没有权限"));
            String userId = JwtUtils.getSubjectFromToken(token);
            redisTemplate.delete("AccessUserService::" + userId);
            return ;
        }

        if (authentication != null) {
//            System.out.println("authentication != null  ");
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        chain.doFilter(req, res);
    }

    /**
     * 授权
     * 根据token获取redis的key （用户名/id） 查询redis得到权限列表
     * (权限列表在 TokenLoginFilter.successfulAuthentication 方法写入)
     * @param token
     * @return
     */
    private UsernamePasswordAuthenticationToken getAuthentication(String token) {
//        System.out.println("getAuthentication ===> " + token);
        if (token != null && !token.trim().equals("")) {
            String userId = tokenManager.getUserIdFromToken(token);
//            System.out.println("userId ===> " + userId);
            if (StringUtils.isEmpty(userId))
                return null;
            String redisToken = (String) redisTemplate.opsForValue()
                    .get("AccessUserService::" + userId);
//            System.out.println("getHeader ==> " + token);
//            System.out.println("redisToken ==> " + redisToken);
            // 与redis存储的不一致时就拒绝访问
            if (! token.equals(redisToken))
                return null;

            // 获取redis中缓存的权限列表 若为空则重新查询
            List<String> permissionValueList =
                    (List<String>) redisTemplate.opsForValue()
                            .get("AccessPermissionService::" + userId);



//            for (int i = 0; i < permissionValueList.size(); i++) {
//                System.out.println("getAuthentication => " + i + " permissionValue: " + permissionValueList.get(i));
//            }

            Collection<GrantedAuthority> authorities = new ArrayList<>();
            if (permissionValueList == null || permissionValueList.isEmpty()) {
                System.out.println("permissionValueList null ");
                // 不能返回空 返回空 catch 不到错误 不会删除redis中的缓存信息
                throw new NullPointerException();
            }
            for (String permissionValue : permissionValueList) {
//                System.out.println("getAuthentication => " + permissionValue);
                if (StringUtils.isEmpty(permissionValue))
                    continue;
                SimpleGrantedAuthority authority =
                        new SimpleGrantedAuthority(permissionValue);
                authorities.add(authority);
            }

//          存入 name authorities principal
//            System.out.println("ddd userId not null");
//            System.out.println(userId);
//            System.out.println(token);
//            System.out.println(authorities);
            return new UsernamePasswordAuthenticationToken(userId, token, authorities);
        }
        return null;
    }
}